package com.ml.mall.service.sys.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ml.mall.bean.base.ApiRestResult;
import com.ml.mall.bean.page.Page;
import com.ml.mall.bean.req.company.CompanyInviteCreateUserDto;
import com.ml.mall.bean.req.company.CompanyRelationAddDto;
import com.ml.mall.bean.req.sys.*;
import com.ml.mall.bean.res.sys.SysUserRes;
import com.ml.mall.bean.res.sys.UserQueryInfoRes;
import com.ml.mall.consts.CommonConsts;
import com.ml.mall.entity.company.SysUserCompanyRelation;
import com.ml.mall.entity.sys.SysPermission;
import com.ml.mall.entity.sys.SysRoleUser;
import com.ml.mall.entity.sys.SysUser;
import com.ml.mall.enums.CommonEnum;
import com.ml.mall.enums.RedisUtilsCacheEnum;
import com.ml.mall.enums.RetStatusNum;
import com.ml.mall.exception.BizException;
import com.ml.mall.mapper.company.SysCompanyMapper;
import com.ml.mall.mapper.company.SysUserCompanyRelationMapper;
import com.ml.mall.mapper.sys.SysPermissionMapper;
import com.ml.mall.mapper.sys.SysRoleMapper;
import com.ml.mall.mapper.sys.SysRoleUserMapper;
import com.ml.mall.mapper.sys.SysUserMapper;
import com.ml.mall.security.JwtUtil;
import com.ml.mall.service.asyn.AuthMailServiceImplAsyn;
import com.ml.mall.service.sys.SysUserService;
import com.ml.mall.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

import static com.ml.mall.consts.CommonConsts.DEFAULT_PWD;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author kyyang
 * Created 2021-01-05
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    @Autowired
    private RedisUtil redisUtil;
    @Value("${jwt.token.expire.time}")
    private Long tokenExpireTime;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysUserCompanyRelationMapper sysUserCompanyRelationMapper;

    @Autowired
    private SysCompanyMapper sysCompanyMapper;

    @Autowired
    private SysRoleUserMapper sysRoleUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Lazy
    @Autowired
    private AuthMailServiceImplAsyn authMailServiceAsyn;


    @Override
    public boolean refreshTokenIsValid(String oldToken) {
        SysUser userByToken = JwtUtil.getUserByToken(oldToken);
        String refreshTokenTime =(String) redisUtil.get( CommonConsts.USER_REDIS_KEY+userByToken.getAcc()+userByToken.getSalt());
        if (refreshTokenTime == null) {
            return false;
        }
        JSONObject jsonObject = JSON.parseObject(refreshTokenTime);
        Long expire = jsonObject.getLong("expire");
        String token = jsonObject.getString("token");
        if (!oldToken.equals(token)) {
            return false;
        }
        return System.currentTimeMillis() <= expire;
    }

    @Override
    public String loginForToken(SysUser user) {
        Long holdTime = tokenExpireTime * 24 * 1000;  //过期时间24小时  holdTime为毫秒
        //获取用户token值
        String token = JwtUtil.sign(user, holdTime);
        //将token作为RefreshToken Key 存到缓存中，缓存时间为token有效期的两倍
        String refreshTokenCacheKey = token;
        Date expireDate = new Date(System.currentTimeMillis() + holdTime);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("token", refreshTokenCacheKey);
        jsonObject.put("expire", expireDate.getTime());
        redisUtil.set(CommonConsts.USER_REDIS_KEY+user.getAcc()+user.getSalt(), jsonObject.toJSONString(),holdTime );
        log.info("token:{}", token);
        return token;
    }

    @Override
    public SysUser findByAccount(String userName) {
        return sysUserMapper.selectByAccount(userName);
    }

    @Override
    public SysUser getById(Long userId) {
        return sysUserMapper.selectById(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int save(SysUserDto sysUserDto) {
        SysUser sysUser = new SysUser();
        //默认密码  与账号一致
        sysUser.setAcc(sysUserDto.getAcc());
        Integer state = sysUserDto.getState()==null? 0:sysUserDto.getState();
        sysUser.setState(state);
        sysUser.setName(sysUserDto.getName());
        sysUser.setSalt(StrUtils.getUUID());
        sysUser.setRoleId(sysUserDto.getRoleId());
        sysUser.setCompanyId(sysUserDto.getCompanyId());
        sysUser.setLastName(sysUserDto.getLastName());
        sysUser.setTelNumber(sysUserDto.getTelNumber());
        sysUser.setRoleType(sysUserDto.getRoleType());
        sysUser.setHeadUrl(sysUserDto.getHeadUrl());
        sysUser.setEmail(sysUserDto.getEmail());
        sysUser.setPwd(MD5.md5(DEFAULT_PWD, sysUser.getSalt()));
        sysUser.setTelNumber(sysUserDto.getTelNumber());
        sysUserMapper.insert(sysUser);
        //获取该用户ID
        SysUser ml = sysUserMapper.selectByAccount(sysUserDto.getAcc());
        SysRoleUser ent = new SysRoleUser();
        ent.setUserId(ml.getId());
        ent.setRoleId(sysUserDto.getRoleId());
        int isAdd = sysRoleUserMapper.insert(ent);
        return isAdd;
    }

    @Override
    public int addNewUser(SysUserDto sysUserDto) {
        SysUser curUser = JwtUtil.getCurrentUser();
        SysUser sysUser = new SysUser();
        sysUser.setAcc(sysUserDto.getAcc());
        sysUser.setState(0);//默认正常
        sysUser.setName(sysUserDto.getName());
        sysUser.setSalt(StrUtils.getUUID());
        sysUser.setRoleId(sysUserDto.getRoleId());
        sysUser.setCompanyId(curUser.getCompanyId());
        sysUser.setLastName(sysUserDto.getLastName());
        sysUser.setRoleType(curUser.getRoleType());
        sysUser.setHeadUrl(curUser.getHeadUrl());
        sysUser.setEmail(sysUserDto.getAcc());
        sysUser.setPwd(MD5.md5(DEFAULT_PWD, sysUser.getSalt()));
        sysUserMapper.insert(sysUser);
        //获取该用户ID
        SysUser ml = sysUserMapper.selectByAccount(sysUserDto.getAcc());
        SysRoleUser ent = new SysRoleUser();
        ent.setUserId(ml.getId());
        ent.setRoleId( sysUserDto.getRoleId());
        int isAdd = sysRoleUserMapper.insert(ent);
        //发送邮件
        authMailServiceAsyn.userSaveMailAsyn(sysUserDto);
        return isAdd;
    }

    @Override
    public Page<SysUserRes> selectAccByPage(SysUserQueryDto dto) {
        List<SysUserRes> userResList = sysUserMapper.selectCompanyUser(dto);
        Integer userCount = sysUserMapper.countCompanyUser(dto);

        Page<SysUserRes> resPage = new Page<>(dto.getPageIndex(), dto.getPageNum());
        resPage.setTotal(userCount);
        resPage.setRecords(userResList);
        return resPage;
    }

    @Override
    @Transactional
    public int editInfo(SysUserEditDto sysUserEditDto) {
        SysUser sysUser = new SysUser();
        sysUser.setUpdateAt(new Date());
        UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<SysUser>();

        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getHeadUrl())) {
            updateWrapper.set("head_url", sysUserEditDto.getHeadUrl());
        }

        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getName())) {
            updateWrapper.set("name", sysUserEditDto.getName());
        }
        if (sysUserEditDto.getCompanyId()!=null) {
            updateWrapper.set("company_id", sysUserEditDto.getCompanyId());
        }
        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getTelNumber())) {
            updateWrapper.set("tel_Number", sysUserEditDto.getTelNumber());
        }
        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getState())) {
            updateWrapper.set("state", sysUserEditDto.getState());
        }
        if (org.apache.commons.lang3.StringUtils.isNotBlank(sysUserEditDto.getLastName())){
            updateWrapper.set("last_name", sysUserEditDto.getLastName());
        }

        updateWrapper.eq("id", sysUserEditDto.getId().intValue());
        int update = sysUserMapper.update(sysUser, updateWrapper);
        return update;
    }

    @Override
    @Transactional
    public int editResetpwd(SysUser sysUser) {
        sysUser.setUpdateAt(new Date());
        sysUser.setPwd(MD5.md5(DEFAULT_PWD, sysUser.getSalt()));
        int i = sysUserMapper.updateById(sysUser);
        return i;
    }

    @Override
    @Transactional
    public int editPwd(SysUser byAccount, SysUserEditPwdDto sysUserEditPwdDto) {
        byAccount.setUpdateAt(new Date());
        String s = MD5.md5(sysUserEditPwdDto.getNewpwd(), byAccount.getSalt());
        byAccount.setPwd(s);
        int i = sysUserMapper.updateById(byAccount);
        return i;
    }

    @Override
    public void logout() {
        SysUser currentUser = JwtUtil.getCurrentUser();
        redisUtil.del(CommonConsts.USER_REDIS_KEY+currentUser.getAcc()+currentUser.getSalt());
    }

    @Override
    public List<SysPermission> accountPermissionsQuery(SysPermissionsDto sysPermissionsDto) {
        Integer permissionId = sysPermissionsDto.getPermissionId();
        if(permissionId==null){
            permissionId=0;
        }
        List<SysPermission> list = sysUserMapper.selectCurrentPer(JwtUtil.getUserId(),permissionId);
        return list;
    }

    @Override
    public List<SysPermission> accountMenuQuery() {
        List<SysPermission> list = sysUserMapper.selectCurrentMenu(JwtUtil.getUserId());
        list.parallelStream().forEach((per) -> {
            List<SysPermission> child = list.stream().filter((p) -> {
                return p.getParentId().equals(per.getId());
            }).collect(Collectors.toList());
            per.setChild(child);
        });
        return list.stream().filter((p) -> {
            return p.getParentId()==0;
        }).collect(Collectors.toList());
    }

    @Override
    public UserQueryInfoRes queryOneSysUser(SysUserQueryOneDto dto) {
        //查询用户信息
        UserQueryInfoRes userQueryInfoRes = null;
        if(dto.getId() != null || dto.getAcc() != null){
            userQueryInfoRes = sysUserMapper.selectOne(dto);
        }

        SysUser sysUser = null;
        if(userQueryInfoRes == null){
            userQueryInfoRes = new UserQueryInfoRes();
            sysUser = JwtUtil.getCurrentUser();
            BeanUtils.copyProperties(sysUser,userQueryInfoRes);
        }

        return userQueryInfoRes;
    }


    /**
     * 生成验证码
     * @param i 验证码长度
     * @return
     */
    private static String getValidate(int i) {
        Random random = new Random();
        String num = random.nextInt(10000) + "";
        // 这一步很关键，将生成的num赋值给另一个字符串，用以当做固定值。
        String finalNum = num;
        if (num.length() < i) {
            for (int j = 1; j <= i - finalNum.length(); j++) {
                num = "0" + num;  // 做字符串的拼接。不足4位前面补0
            }
        }
        return num;
    }


    /**
     * 通过邀请创建用户
     * @param dto
     * @return
     */
    @Override
    public int createUserByInvite(CompanyInviteCreateUserDto dto) {
        String userCompanyRelationKey = RedisUtilsCacheEnum.REDIS_KEY_USER_COMPANY_RELATION.getKeyPrefix()+ UserCompanyRelationUtil.getCurUserCompanyRelationKey();
        //获取redis中存储的信息
        long redisDataSize = redisUtil.lGetListSize(userCompanyRelationKey);
        List<Object> getRedisAcc = redisUtil.lGet(userCompanyRelationKey, 0, redisDataSize-1);
        int i =1; //用于计数，判断是否存在该acc
        for (Object item:getRedisAcc){
            CompanyRelationAddDto redisGetCompanyRelationData = JSON.parseObject((String) item, CompanyRelationAddDto.class);
            log.info("redisGetCompanyRelationData：{}",redisGetCompanyRelationData.toString());
            if (redisGetCompanyRelationData.getAcc().equals(dto.getAcc())){
                QueryWrapper<SysUser> querySameWrapper = new QueryWrapper<>();
                querySameWrapper.eq("acc",redisGetCompanyRelationData.getAcc());
                Integer ifDbHave = sysUserMapper.selectCount(querySameWrapper);
                if (ifDbHave!=0){
                    throw new BizException(CommonEnum.ALREADY_EXIST_SAME);
                }
                //创建新用户，该协议是指用户接收到邮件；点击链接 为公司添加账号；
                //业务：原有：acc,companyId,name,lastName,roleId；需要新添加：email=acc,salt=StrUtils.getUUID,pwd,roleType=companyType,state=0
                SysUser needToSave = new SysUser();
                needToSave.setAcc(redisGetCompanyRelationData.getAcc())
                    .setCompanyId(redisGetCompanyRelationData.getCompanyId())
                    .setName(redisGetCompanyRelationData.getName())
                    .setLastName(redisGetCompanyRelationData.getLastName())
                    .setRoleId(redisGetCompanyRelationData.getRoleId())
                    .setEmail(redisGetCompanyRelationData.getAcc())
                    .setSalt(StrUtils.getUUID())
                    .setPwd(MD5.md5(dto.getPwd(),needToSave.getSalt()))
                    .setRoleType(Integer.parseInt(sysCompanyMapper.selectInviteDataByCompanyId(needToSave.getCompanyId())))
                    .setState(0);
                int ifSuccess = sysUserMapper.insert(needToSave);
                if (ifSuccess==0){
                    throw new BizException(CommonEnum.INSERT_ERROR);
                }
                break;
            }
            i++;//假设redis里面有两条数据，但是都没有遍历找到同样的acc，i==3
            if (i==redisDataSize+1){
                throw new BizException(CommonEnum.CHECK_IF_ACC_RIGHT);
            }
        }
        return 1;
    }


    @Override
    public int accountChangeCompany(SysUserChangeCompanyDto dto) {
        //用于切换账号归属的公司：
        //业务阐述：
        //查找该用户Jwt下对应 acc 与传入的companyId 是否存在表sys_user_company_relation中是否攒在 ；
        //如果存在更新 sys_user.company_id 为传入id sys_user.role_id
        SysUser user = JwtUtil.getCurrentUser();
        String acc = user.getAcc();
        Long companyId = user.getCompanyId();

        //查找在sys_user_company_relation中是否存在该用户
        QueryWrapper<SysUserCompanyRelation> ifUserCompanyRelationExistWrapper = new QueryWrapper<>();
        ifUserCompanyRelationExistWrapper.eq("acc",acc)
            .eq("company_id",companyId);
        Integer ifExist = sysUserCompanyRelationMapper.selectCount(ifUserCompanyRelationExistWrapper);
        if (ifExist==0){
            throw new BizException(CommonEnum.CONNECT_WITH_COMPANY);
        }

        //执行更改账户归属公司操作
        UpdateWrapper<SysUser> changeCompanyWrapper = new UpdateWrapper<>();
        changeCompanyWrapper.eq("acc",acc)
            .eq("company_id",companyId);
        SysUser updateUser = new SysUser();
        updateUser.setRoleId(dto.getRoleId())
            .setRoleType(Integer.parseInt(sysCompanyMapper.selectInviteDataByCompanyId(dto.getCompanyId())))
            .setCompanyId(dto.getCompanyId());
        int ifUpdateSuccess = sysUserMapper.update(updateUser, changeCompanyWrapper);
        if (ifUpdateSuccess==0){
            throw new BizException(CommonEnum.UPDATE_ERROR);
        }
        return 1;
    }


    @Override
    public void forgetPasswd2Email(SysUserForgetPasswdEmail sysUserForgetPasswdEmail) {
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
        String code = lineCaptcha.getCode();
        String email = sysUserForgetPasswdEmail.getAcc();
        //传入的邮箱去除所有空白字符
        email = email.replaceAll("\\s", "");

        redisUtil.set(RedisUtilsCacheEnum.REDIS_KEY_USER_FORGET_PWD.getKeyPrefix()+ email,code,RedisUtilsCacheEnum.REDIS_KEY_USER_FORGET_PWD.getExpiresSecond());

        //根据邮箱获取用户
        SysUser dbUser = sysUserMapper.selectAccByEmail(email);
        if(null == dbUser){
            throw new BizException(CommonEnum.NO_EXISTS_USER);
        }
        authMailServiceAsyn.forgetPasswd2Email(code, dbUser);
    }

    @Override
    public ApiRestResult forgetPasswd(SysUserForgetPasswdDto dto) {
        String key = RedisUtilsCacheEnum.REDIS_KEY_USER_FORGET_PWD.getKeyPrefix() + dto.getAcc();
        String codeStr = String.valueOf( redisUtil.get(key) );
        log.info("code====={}",codeStr);
        if(!dto.getCode().equalsIgnoreCase(codeStr)||codeStr==null){
            return ApiRestResult.err(RetStatusNum.USER_FOREGET_PWD_ERROR.getCode(),RetStatusNum.USER_FOREGET_PWD_ERROR.getMessage());
        }
        redisUtil.del( key );
        //重置密码
        //查找账号
        SysUser sysUser = sysUserMapper.selectAccByEmail(dto.getAcc());
        sysUser.setUpdateAt(new Date());
        String s = MD5.md5(dto.getPassword(), sysUser.getSalt());
        sysUser.setPwd(s);
        sysUserMapper.updateById(sysUser);
        return ApiRestResult.ok();
    }

    @Override
    public List<SysUser> queryByName(SysQuerySameNameDto sysQuerySameNameDto) {
        QueryWrapper<SysUser> w = new QueryWrapper<>();
        w.eq("name",sysQuerySameNameDto.getName());
        List<SysUser> sysUsers = sysUserMapper.selectList(w);
        return sysUsers;
    }

    @Override
    public Page<SysUserRes> selectSelfCompanyAccByPage(SysUserQueryDto dto) {
        //限定查询本公司用户
        SysUser currentUser = JwtUtil.getCurrentUser();
        dto.setCompanyId(currentUser.getCompanyId());
        return selectAccByPage(dto);
    }

    @Override
    @Transactional
    public boolean saveUserAndRoleUser(SysUser sysUser) {
        boolean isAdded = save(sysUser);
        if(isAdded) {
            SysRoleUser sysRoleUser = new SysRoleUser();
            sysRoleUser.setUserId(sysUser.getId());
            sysRoleUser.setRoleId(sysUser.getRoleId());
            sysRoleUserMapper.insert(sysRoleUser);
        }
        return isAdded;
    }

    @Override
    public SysUser selectAccByEmail(String email) {
        return sysUserMapper.selectAccByEmail(email);
    }

//    @Override
//    public boolean resetPassword(String email, String code) {
//        //根据邮箱查找用户
//        SysUser sysUser = sysUserMapper.selectOneByEmail(email);
//        if(null == sysUser){
//            throw new BizException(CommonEnum.NO_EXISTS_USER);
//        }
//        Object redisCode = redisUtil.get(RedisUtilsCacheEnum.REDIS_KEY_USER_FORGET_PWD.getKeyPrefix() + sysUser.getAcc());
//        if(null == redisCode){
//            return false;
//        }
//        if(redisCode.toString().equals(code)){
//            return true;
//        }
//        return false;
//    }
}
